package com.ushine.web.communication.tcp;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.InetSocketAddress;

/**
 * @Author: yls
 * @Date 2022/5/9 9:23
 * @Description: 初始化【netty】服务
 * @Version 1.0
 **/
@Slf4j
@Component
public class NettyWebsocketServer implements
        ApplicationListener<ContextClosedEvent>, ApplicationContextAware {

    private static final  String TAG = "netty-websocket 通信服务端=》";

    /**
     * netty 配置
     */
    @Resource
    private NettyWebsocketConfig nettyWebsocketConfig;

    /**
     * 上下文
     */
    private ApplicationContext applicationContext;

    /**
     * 消息通道
     */
    private Channel channel;


    /**
     * 功能描述:
     * 〈 初始化netty服务 〉
     *
     * @return : void
     * @throws InterruptedException 异常信息
     * @author : yls
     * @date : 2022/5/9 9:43
     */
    public void initServer() throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            /*
             * 获取配置信息
             */
            String path = nettyWebsocketConfig.getPath();
            int maxFrameSize = nettyWebsocketConfig.getMaxFrameSize();
            String ip = nettyWebsocketConfig.getIp();
            Integer port = nettyWebsocketConfig.getPort();
            log.info("{}tcp服务端配置信息：{}",TAG, nettyWebsocketConfig);
            /*
             * 初始化服务端
             */
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup);
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.localAddress(new InetSocketAddress(ip, port));
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    pipeline.addLast(new HttpServerCodec());
                    pipeline.addLast(new ChunkedWriteHandler());
                    pipeline.addLast(new HttpObjectAggregator(65536));
                    pipeline.addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            if (msg instanceof FullHttpRequest) {
                                FullHttpRequest fullHttpRequest = (FullHttpRequest) msg;
                                String uri = fullHttpRequest.uri();
                                if (!uri.equals(path)) {
                                    // 访问的路径不是 websocket的端点地址，响应404
                                    ctx.channel().writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND))
                                            .addListener(ChannelFutureListener.CLOSE);
                                    return;
                                }
                            }
                            super.channelRead(ctx, msg);
                        }
                    });
                    pipeline.addLast(new WebSocketServerCompressionHandler());
                    pipeline.addLast(new WebSocketServerProtocolHandler(path, null, true, maxFrameSize));

                    //从IOC中获取到Handler
                    pipeline.addLast(applicationContext.getBean(NettyMessageHandler.class));
                }
            });
            Channel channel = serverBootstrap.bind().sync().channel();
            this.channel = channel;
            log.info("{}启动成功，ip={},port={}",TAG, ip, port);
            channel.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    @Override
    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void onApplicationEvent(@NotNull ContextClosedEvent contextClosedEvent) {
        if (this.channel != null) {
            this.channel.closeFuture();
        }
        log.info("{}服务端停止服务！",TAG);
    }
}
